//C- -------------------------------------------------------------------
//C- Java DjVu (r) (v. 0.8)
//C- Copyright (c) 2004-2005 LizardTech, Inc.  All Rights Reserved.
//C- Java DjVu is protected by U.S. Pat. No.C- 6,058,214 and patents
//C- pending.
//C-
//C- This software is subject to, and may be distributed under, the
//C- GNU General Public License, Version 2. The license should have
//C- accompanied the software or you may obtain a copy of the license
//C- from the Free Software Foundation at http://www.fsf.org .
//C-
//C- The computer code originally released by LizardTech under this
//C- license and unmodified by other parties is deemed "the LIZARDTECH
//C- ORIGINAL CODE."  Subject to any third party intellectual property
//C- claims, LizardTech grants recipient a worldwide, royalty-free,
//C- non-exclusive license to make, use, sell, or otherwise dispose of
//C- the LIZARDTECH ORIGINAL CODE or of programs derived from the
//C- LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
//C- General Public License.   This grant only confers the right to
//C- infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
//C- the extent such infringement is reasonably necessary to enable
//C- recipient to make, have made, practice, sell, or otherwise dispose
//C- of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
//C- any greater extent that may be necessary to utilize further
//C- modifications or combinations.
//C-
//C- The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
//C- OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
//C- TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
//C- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
//C-
//C- In addition, as a special exception, LizardTech Inc. gives permission
//C- to link the code of this program with the proprietary Java
//C- implementation provided by Sun (or other vendors as well), and
//C- distribute linked combinations including the two. You must obey the
//C- GNU General Public License in all respects for all of the code used
//C- other than the proprietary Java implementation. If you modify this
//C- file, you may extend this exception to your version of the file, but
//C- you are not obligated to do so. If you do not wish to do so, delete
//C- this exception statement from your version.
//C- -------------------------------------------------------------------
//C- Developed by Bill C. Riemers, Foxtrot Technologies Inc. as work for
//C- hire under US copyright laws.
//C- -------------------------------------------------------------------
//
package com.lizardtech.djvu{
import java.io.IOException;


public class IWCodec
  extends DjVuObject
{
  //~ Static fields/initializers ---------------------------------------------

  
  public const iw_quant:Array= [
    0x10000, 0x20000, 0x20000, 0x40000, 0x40000, 0x40000, 0x80000, 0x80000,
    0x80000, 0x100000, 0x100000, 0x100000, 0x200000, 0x100000, 0x100000, 0x200000];
    
  /*  
  public const iw_quant:Array= [
    int(0x10000), int(0x20000), int(0x20000), int(0x40000), int(0x40000), int(0x40000), int(0x80000), int(0x80000),
    int(0x80000), int(0x100000), int(0x100000), int(0x100000), int(0x200000), int(0x100000), int(0x100000),
    int(0x200000)];
  */

  public const bandbuckets:Array=
  [
    new Bucket(0, 1), new Bucket(1, 1), new Bucket(2, 1), new Bucket(3, 1),
    new Bucket(4, 4), new Bucket(8, 4), new Bucket(12, 4), new Bucket(16, 16),
    new Bucket(32, 16), new Bucket(48, 16)
  ];

  static const ZERO:int= 1;
  static const ACTIVE:int= 2;
  static const NEW:int= 4;
  static const UNK:int= 8;

  //~ Instance fields --------------------------------------------------------

  private var ctxMant:BitContext;
  private var ctxRoot:BitContext;
  private var map:IWMap;
  private var bucketstate:Array = new Array();
  private var coeffstate:Array = new Array();
  private var ctxBucket:Array = new Array();
  private var ctxStart:Array = new Array();
  private var quant_hi:Array = new Array();
  private var quant_lo:Array = new Array();
  private var curband:int;
  private var curbit:int;
  private var initialized:Boolean;

  //~ Constructors -----------------------------------------------------------

  /**
   * Creates a new IWCodec object.
   */
  public function IWCodec(map:IWMap=null)
  {
  	
  	if(map != null) {
  		init(map);
  	}
  	
  	initialized = true;
    //ctxStart = new BitContext[32];
    ctxStart = new Array(32);
    for(var i:int= 0; i < 32; i++)
    {
      ctxStart[i] = new BitContext();
    }

    //ctxBucket = new BitContext[10][8];
    ctxBucket = new Array(10);

    for(var i:int= 0; i < 10; i++)
    {
      ctxBucket[i] = new Array(8);
      for(var j:int= 0; j < 8; j++)
      {
        ctxBucket[i][j] = new BitContext();
      }
    }

    quant_hi      = new Array(10);//int[10];
    quant_lo      = new Array(16);//int[16];
    coeffstate    = new Array(256); //byte[256];
    bucketstate   = new Array(16); //byte[16];
    curband       = 0;
    curbit        = 1;
    ctxMant       = new BitContext();
    ctxRoot       = new BitContext();
  }

  //~ Methods ----------------------------------------------------------------

  /**
   * Creates an instance of IWCodec with the options interherited from the
   * specified reference.
   *
   * @param ref Object to interherit DjVuOptions from.
   *
   * @return a new instance of IWCodec.
   */
  public static function createIWCodec(ref:DjVuInterface):IWCodec{
    var options:DjVuOptions= ref.getDjVuOptions();

    return create(
      options,
      options.getIWCodecClass(),
      "com.lizardtech.djvu.IWCodec") as IWCodec;
  }

  /**
   * DOCUMENT ME!
   *
   * @param zp DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   *
   * @throws IOException DOCUMENT ME!
   */
  public function code_slice(zp:ZPCodec):int
  {
    if(curbit < 0)
    {
      return 0;
    }

    if(is_null_slice(curbit, curband) == 0)
    {
      for(var blockno:int= 0; blockno < map.nb; blockno++)
      {
        var fbucket:int= bandbuckets[curband].start;
        var nbucket:int= bandbuckets[curband].size;
        decode_buckets(
          zp,
          curbit,
          curband,
          map.blocks[blockno],
          fbucket,
          nbucket);
      }
    }

    if(++curband >= bandbuckets.length)
    {
      curband = 0;
      curbit++;

      if(next_quant() == 0)
      {
        curbit = -1;

        return 0;
      }
    }

    return 1;
  }

  /**
   * DOCUMENT ME!
   *
   * @param zp DOCUMENT ME!
   * @param bit DOCUMENT ME!
   * @param band DOCUMENT ME!
   * @param blk DOCUMENT ME!
   * @param fbucket DOCUMENT ME!
   * @param nbucket DOCUMENT ME!
   *
   * @throws IOException DOCUMENT ME!
   */
  private function decode_buckets(
    zp:ZPCodec,
    bit:int,
    band:int,
    blk:IWBlock,
    fbucket:int,
    nbucket:int):void
  {
    var thres:int= quant_hi[band];
    var bbstate:int= 0;
    var cstate:Array= coeffstate;
    var cidx:int= 0;

    for(var buckno:int= 0; buckno < nbucket;)
    {
      var bstatetmp:int= 0;
      var pcoeff:Array= blk.getBlock(fbucket + buckno);

      if(pcoeff == null)
      {
        bstatetmp = 8;
      }
      else
      {
        for(var i:int= 0; i < 16; i++)
        {
          var cstatetmp:int= cstate[cidx + i] & 1;

          if(cstatetmp == 0)
          {
            if(pcoeff[i] != 0)
            {
              cstatetmp |= 2;
            }
            else
            {
              cstatetmp |= 8;
            }
          }

          cstate[cidx + i] = int(cstatetmp);
          bstatetmp |= cstatetmp;
        }
      }

      bucketstate[buckno] = int(bstatetmp);
      bbstate |= bstatetmp;
      buckno++;
      cidx += 16;
    }

    if((nbucket < 16) || ((bbstate & 2) != 0))
    {
      bbstate |= 4;
    }
    else if((bbstate & 8) != 0)
    {
      if(zp.decoder(ctxRoot) != 0)
      {
        bbstate |= 4;
      }
    }

    if((bbstate & 4) != 0)
    {
      for(var buckno:int= 0; buckno < nbucket; buckno++)
      {
        if((bucketstate[buckno] & 8) != 0)
        {
          var ctx:int= 0;

          if(!DjVuOptions.NOCTX_BUCKET_UPPER && (band > 0))
          {
            var k:int= (fbucket + buckno) << 2;
            var b:Array= blk.getBlock(k >> 4);

            if(b != null)
            {
              k &= 0xf;

              if(b[k] != 0)
              {
                ctx++;
              }

              if(b[k + 1] != 0)
              {
                ctx++;
              }

              if(b[k + 2] != 0)
              {
                ctx++;
              }

              if((ctx < 3) && (b[k + 3] != 0))
              {
                ctx++;
              }
            }
          }

          if(!DjVuOptions.NOCTX_BUCKET_ACTIVE && ((bbstate & 2) != 0))
          {
            ctx |= 4;
          }

		  //trace("ctxBucket[" + band + "][" + ctx + "]="+(ctxBucket[band][ctx] as BitContext).bit);
		  var rbit:int = zp.decoder((ctxBucket[band][ctx]) as BitContext);
          if(rbit != 0)
          {
            bucketstate[buckno] |= 4;
          }
        }
      }
    }

    if((bbstate & 4) != 0)
    {
      cstate   = coeffstate;
      cidx     = 0;

      for(var buckno:int= 0; buckno < nbucket;)
      {
        if((bucketstate[buckno] & 4) != 0)
        {
          var pcoeff:Array= blk.getBlock(fbucket + buckno);

          if(pcoeff == null)
          {
            pcoeff = blk.getInitializedBlock(fbucket + buckno);

            for(var i:int= 0; i < 16; i++)
            {
              if((cstate[cidx + i] & 1) == 0)
              {
                cstate[cidx + i] = 8;
              }
            }
          }

          var gotcha:int= 0;
          var maxgotcha:int= 7;

          if(!DjVuOptions.NOCTX_EXPECT)
          {
            for(var i:int= 0; i < 16; i++)
            {
              if((cstate[cidx + i] & 8) != 0)
              {
                gotcha++;
              }
            }
          }

          for(var i:int= 0; i < 16; i++)
          {
            if((cstate[cidx + i] & 8) != 0)
            {
              if(band == 0)
              {
                thres = quant_lo[i];
              }

              var ctx:int= 0;

              if(!DjVuOptions.NOCTX_EXPECT)
              {
                if(gotcha >= maxgotcha)
                {
                  ctx = maxgotcha;
                }
                else
                {
                  ctx = gotcha;
                }
              }

              if(
                !DjVuOptions.NOCTX_ACTIVE
                && ((bucketstate[buckno] & 2) != 0))
              {
                ctx |= 8;
              }

              if(zp.decoder(ctxStart[ctx]) != 0)
              {
                cstate[cidx + i] |= 4;

                var halfthres:int= thres >> 1;
                var coeff:int= (thres + halfthres) - (halfthres >> 2);

                if(zp.IWdecoder() != 0)
                {
                  pcoeff[i] = int((-coeff));
                }
                else
                {
                  pcoeff[i] = int(coeff);
                }
              }

              if(!DjVuOptions.NOCTX_EXPECT)
              {
                if((cstate[cidx + i] & 4) != 0)
                {
                  gotcha = 0;
                }
                else if(gotcha > 0)
                {
                  gotcha--;
                }
              }
            }
          }
        }

        buckno++;
        cidx += 16;
      }
    }

    if((bbstate & 2) != 0)
    {
      cstate   = coeffstate;
      cidx     = 0;

      for(var buckno:int= 0; buckno < nbucket;)
      {
        if((bucketstate[buckno] & 2) != 0)
        {
          var pcoeff:Array= blk.getBlock(fbucket + buckno);

          for(var i:int= 0; i < 16; i++)
          {
            if((cstate[cidx + i] & 2) != 0)
            {
              var coeff:int= pcoeff[i];

              if(coeff < 0)
              {
                coeff = -coeff;
              }

              if(band == 0)
              {
                thres = quant_lo[i];
              }

              if(coeff <= (3* thres))
              {
                coeff += (thres >> 2);

                if(zp.decoder(ctxMant) != 0)
                {
                  coeff += (thres >> 1);
                }
                else
                {
                  coeff = (coeff - thres) + (thres >> 1);
                }
              }
              else
              {
                if(zp.IWdecoder() != 0)
                {
                  coeff += (thres >> 1);
                }
                else
                {
                  coeff = (coeff - thres) + (thres >> 1);
                }
              }

              if(pcoeff[i] > 0)
              {
                pcoeff[i] = int(coeff);
              }
              else
              {
                pcoeff[i] = int((-coeff));
              }
            }
          }
        }

        buckno++;
        cidx += 16;
      }
    }
  }

  /**
   * DOCUMENT ME!
   *
   * @param frac DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  function estimate_decibel(frac:Number):Number{
    return 0.0;
  }

  /**
   * DOCUMENT ME!
   *
   * @param map DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  function init(map:IWMap):IWCodec{
    this.map = map;

    var i:int= 0;
    var q:Array= iw_quant;
    var qidx:int= 0;

    for(var j:int= 0; i < 4; j++)
    {
      quant_lo[i++] = q[qidx++];
    }

    for(var j:int= 0; j < 4; j++)
    {
      quant_lo[i++] = q[qidx];
    }

    qidx++;

    for(var j:int= 0; j < 4; j++)
    {
      quant_lo[i++] = q[qidx];
    }

    qidx++;

    for(var j:int= 0; j < 4; j++)
    {
      quant_lo[i++] = q[qidx];
    }

    qidx++;
    quant_hi[0] = 0;

    for(var j:int= 1; j < 10; j++)
    {
      quant_hi[j] = q[qidx++];
    }

    while(quant_lo[0] >= 32768)
    {
      next_quant();
    }

    return this;
  }

  /**
   * DOCUMENT ME!
   *
   * @param bit DOCUMENT ME!
   * @param band DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  private function is_null_slice(
    bit:int,
    band:int):int{
    if(band == 0)
    {
      var is_null:int= 1;

      for(var i:int= 0; i < 16; i++)
      {
        var threshold:int= quant_lo[i];
        coeffstate[i] = 1;

        if((threshold > 0) && (threshold < 32768))
        {
          is_null = coeffstate[i] = 0;
        }
      }

      return is_null;
    }

    var threshold:int= quant_hi[band];

    if((threshold <= 0) || (threshold >= 32768))
    {
      return 1;
    }

    for(var i:int= 0; i < (bandbuckets[band].size << 4); i++)
    {
      coeffstate[i] = 0;
    }

    return 0;
  }

  /**
   * DOCUMENT ME!
   *
   * @return DOCUMENT ME!
   */
  function next_quant():int{
    var flag:int= 0;

    for(var i:int= 0; i < 16; i++)
    {
      if((quant_lo[i] = quant_lo[i] >> 1) != 0)
      {
        flag = 1;
      }
    }

    for(var i:int= 0; i < 10; i++)
    {
      if((quant_hi[i] = quant_hi[i] >> 1) != 0)
      {
        flag = 1;
      }
    }

    return flag;
  }

  //~ Inner Classes ----------------------------------------------------------

  /**
   * DOCUMENT ME!
   *
   * @author $author$
   * @version $Revision: 1.4 $
   */
 
}}